package com.multimedia.scala.euler

import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import com.multimedia.scala.Primes
import scala.actors.Actor
import scala.actors.Actor._
/*
The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime.

There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97.

How many circular primes are there below one million?
 */
object Asd35 {
    def main(args : Array[String]) : Unit = {
        val simples = Primes.findSimples(1000000)

        println("primes calculated")
        //simples.filter(isCircularCached)
        //println(simples.size)
        val finder = new CircularFinder(simples)
        finder.start
        //var a = List(4.0, 3.0, 2.5, 2.0, 1.0)
        //a.reverse
        //println(shifts(a).size)
    }

    @tailrec
    def shiftLeft[B](x: List[B], step: Int): List[B] = {
        if (step == 0) x
        else shiftLeft(x.last::x.take(x.size-1), step - 1)  //x.tail::x.head
    }
    
    @tailrec
    def shifts[B](x: List[B], quantity: Int = 0, acc: ListBuffer[List[B]] = ListBuffer[List[B]]()): List[List[B]] = {
        if (quantity == x.size) acc.toList
        else {
            val shifted = x.last::x.take(x.size-1)
            acc += shifted
            shifts(shifted, quantity +1, acc)
        }
    }

    class CircularFinder(primes: ListBuffer[Int], threads: Int = 4) extends Actor {
        var count = primes.size
        var myCache = new scala.collection.mutable.HashSet[Int]()
        var inWait = 0
        
        def act() {
            println("start to calculate")
            val slaves = (1 to threads).map(x=>{new CircularFinderSlave(this, myCache, primes.toSet)})
            slaves.foreach(x=>{
                x.start()
                count -= 1
                inWait += 1
                x ! NextInt(primes(count))
            })
            loop {
                react {
                  case Finished(s) => {
                    this.synchronized({
                      if (!s.isEmpty) {
                          print(count + ":")
	                      s.foreach(y=>print(y + " "))
	                      println
                      } else {
                          println(count + ": rejected")
                      }
                      inWait -= 1
                      myCache ++= s
                      count -= 1
                      if (count>=0) {
                    	  inWait += 1
                    	  sender ! NextInt(primes(count))
                      } else sender ! Stop
                      if (inWait==0){
                    	  println("end calculating")
                    	  println(myCache.size)//myCache + " " + myCache.size)
                          exit()
                      }
                    })
                  }
                }
            }
            //slaves.foreach(x=>{x ! Stop})
        }
    }

    class CircularFinderSlave(master: CircularFinder, myCache: scala.collection.mutable.HashSet[Int], primesSet: Set[Int]) extends Actor {

        def getCircular(x: Int, cache: scala.collection.mutable.HashSet[Int]): Array[Int] = {
            if (cache.contains(x)) {
                return Array()
            }
            var candidates = shifts(x.toString().toCharArray.map(_.toString).toList)
                              .map(_.reduce(_ + _).toInt).toArray
            if (candidates.forall(primesSet.contains(_))) {
                return candidates
            } else {
                return Array()
            }
        }

        def act() {
            //println("started " + this.##)
            loop {
                react {
                  case NextInt(x) => {
                      println("s: "+x)
                      master ! Finished(getCircular(x, myCache))
                  }
                  case Stop => {
                      //println("finished " + this.##)
                      exit()
                  }
                }
            }
        }
    }
    
    case class Stop
    case class NextInt(x: Int)
    case class Finished(res: Array[Int])
}
